package com.woniu.microzuulserver.filter;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.woniu.microcommonsserver.param.ResponseMsg;
import com.woniu.microcommonsserver.util.JWTUtil;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.util.HashMap;
import java.util.Map;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * 身份认证过滤器
 */
@Component
public class IdentityAuthFilter extends ZuulFilter {

    /**
     * 用来指定过滤器的类型
     * pre route post error
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 过滤器的顺序
     * @return 返回的Int越小，越先执行
     * 每个过滤器，默认都有一个中间值5
     */
    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    /**
     * 用来判断哪些路径，是需要做过滤处理的
     * @return true 需要做过滤（就是需要执行run()）  false 不需要做过滤(就是不需要执行run()）
     */
    @Override
    public boolean shouldFilter() {
        //请求上下文对象
        RequestContext requestContext = RequestContext.getCurrentContext();
        //获得请求对象
        HttpServletRequest request = requestContext.getRequest();
        //获得请求路径
        String path = request.getRequestURI();// 某个微服务/请求路径
//        request.getRequestURL();// http://ip:port/某个微服务/请求路径
        System.out.println("path------" + path);
        /*注册方法，登录方法 这些是不需要执行过滤处理的*/
        if(path.equals("/micro-user-server/user/login")){
            return false;
        }
        return true;
    }

    /**
     * 具体的过滤器规则
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        System.out.println("执行过滤方法");
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        //从Http Header中，判断是否有authentication的存在
        String authentication = request.getHeader("authentication");
        System.out.println("从前台得到的token为:"+authentication);
        if(StringUtils.hasLength(authentication)){
            //有长度，暂时默认成功
            System.out.println("进入到微服务");
            //当获取的令牌以后，Zuul需要对Token做一个身份识别
            validateTokenLegal(authentication,requestContext);
        }else{//如果没有长度，或者为NULL，设置响应失败(403)
            handlerIllegalToken(requestContext, HttpStatus.UNAUTHORIZED.value(), ResponseMsg.builder().code(10001).msg("未认证，请登录！").build());
        }
        return null;
    }

    /**
     * 处理非常令牌
     * @param requestContext
     * @param status
     * @param responseMsg
     */
    public void handlerIllegalToken(RequestContext requestContext,int status,ResponseMsg responseMsg){
        //不进入到下一步(route过滤器)
        requestContext.setSendZuulResponse(false);

        //设置响应数据
        HttpServletResponse response = requestContext.getResponse();
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        requestContext.setResponse(response);
        requestContext.setResponseStatusCode(status);
        requestContext.setResponseBody(JSONObject.toJSONString
                (responseMsg));
    }


    /**
     * 用于识别Token令牌是否合法,置换令牌
     * @param authentication
     * @param requestContext
     */
    private void validateTokenLegal(String authentication, RequestContext requestContext) {
        //创建一个JWT的验证实例
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(JWTUtil.SECURITY)).build();
        try{
            //该方法就是针对令牌做校验
            DecodedJWT decodedJWT = verifier.verify(authentication);
            //更新令牌
            Map<String,Object> claims = new HashMap<>();
            String loginName = decodedJWT.getClaim("userName").as(String.class);
            claims.put("userName",loginName);
            String newToken = JWTUtil.createToken(claims);
            //获取到响应对象
            HttpServletResponse response = requestContext.getResponse();
            response.setHeader("token",newToken);
            //暴露token头，给前端
            response.setHeader("Access-Control-Expose-Headers","token");
        } catch (Exception e){
            e.printStackTrace();
            //令牌的算法错误
            if(e instanceof AlgorithmMismatchException){
                handlerIllegalToken(requestContext,HttpStatus.FORBIDDEN.value(),
                        ResponseMsg.builder().code(403).msg("非法令牌，无法识别！").build());
                //令牌已失效
            }else if(e instanceof TokenExpiredException){
                handlerIllegalToken(requestContext,HttpStatus.FORBIDDEN.value(),
                        ResponseMsg.builder().code(403).msg("令牌已过期，请重新登录！").build());
                //令牌是非法令牌
            }else if(e instanceof SignatureVerificationException){
                handlerIllegalToken(requestContext,HttpStatus.FORBIDDEN.value(),
                        ResponseMsg.builder().code(403).msg("非法令牌，无法识别！").build());
            }else{
                handlerIllegalToken(requestContext,HttpStatus.FORBIDDEN.value(),
                        ResponseMsg.builder().code(403).msg("未知错误！").build());
            }
        }
    }
}
